package ngat.oss.simulation;

import ngat.phase2.*;
import ngat.util.*;
import ngat.util.logging.*;
import ngat.astrometry.*;
import ngat.oss.simulation.metrics.*;

import java.util.*;

public class AlternativeScoringModel implements ScoringModel, PropertiesConfigurable {

    public static final double EINC   = Math.toRadians(23.439); // ecliptic inclination
    public static final double DYFRAC = Math.PI/182.625; // conversion

    PriorityUtilityCalculator        puc;
    OptimalAirmassUtilityCalculator  oauc;
    YieldTrackingUtilityCalculator   ytuc;
    RemainingNightsUtilityCalculator rnuc;

    // Models used by utility calculator.
    TimingConstraintWindowCalculator tcwc;
    ExecutionTimingModel execModel;
    ExecutionHistoryModel historyModel;
    Site site;

    // relative weighting.
    public double wgtPriority = 0.0;
    public double wgtOptAir   = 0.0;
    public double wgtYield    = 0.0;
    public double wgtUrg      = 0.0;

    Logger logger;

    /** Used for night length calculation.*/
    Calendar cal;

    /** Create an AlternativeScoringModel using supplied models.*/
    public AlternativeScoringModel(TimingConstraintWindowCalculator tcwc,
				   ExecutionTimingModel execModel,
				   ExecutionHistoryModel historyModel,
				   Site site) {
	this.tcwc         = tcwc;
	this.execModel    = execModel;
	this.historyModel = historyModel;
	this.site         = site;
	
	puc  = new PriorityUtilityCalculator(execModel);
	oauc = new OptimalAirmassUtilityCalculator(site, tcwc, execModel, 5*60*1000L);
	ytuc = new YieldTrackingUtilityCalculator(tcwc, execModel, historyModel, site);
	rnuc = new RemainingNightsUtilityCalculator(tcwc);
	 
	cal = Calendar.getInstance();

	logger = LogManager.getLogger("SIM");
    }

    /** Configure weights.
     * @see ngat.util.PropertiesConfigurable
     */
    public void configure(ConfigurationProperties config) throws Exception {
        // defaults are typical 'sensible' values reflecting scoring
        wgtPriority = config.getDoubleValue("wgt.priority" , 0.4);
        wgtOptAir   = config.getDoubleValue("wgt.optair",     0.25);
        wgtYield    = config.getDoubleValue("wgt.yield",    0.25);
        wgtUrg      = config.getDoubleValue("wgt.urg",    0.2);
    }

    /** Calculate a score for the specified group under specified conditions.
     * @param group    The group to score.
     * @param time     When to calculate the score for.
     * @param accounts The group's accounts.
     * @param env      Environment/sky conditions.
     * @param history  Execution statistics of group upto time.
     */
    public double scoreGroup(Group               group, 
			     Accounts            accounts, 
			     long                time, 
			     EnvironmentSnapshot env, 
			     ExecutionStatistics history) {

	double night = getNightLength(time);

	// NOTE: we scale PX to night length here, should that be done by the PUC ?

	double sPrior  = puc.getUtility(group, time, env, history)/night;
	double sYield  = ytuc.getUtility(group, time, env, history);
	double sOptAir = oauc.getUtility(group, time, env, history);
	double sUrg    = rnuc.getUtility(group, time, env, history);
	
	double score = wgtPriority*sPrior + wgtYield*sYield + wgtOptAir*sOptAir + wgtUrg*sUrg;

	logger.log(1,"Score for: "+group.getName()+" at "+
		   ScheduleSimulator.sdf.format(new Date(time))+
		   " sP="+sPrior+", sY="+sYield+", sOA="+sOptAir+", sU="+sUrg+
		   " Score="+score);
	return score;
    }


    /** Calculate the length of the night at specified time for the site.*/
    public double getNightLength(long time) {

	cal.setTime(new Date(time));
	int doy = cal.get(Calendar.DAY_OF_YEAR);

	double mm = 1.0 - Math.tan(site.getLatitude())*Math.tan(EINC*Math.cos((doy+10)*DYFRAC));
	//if (mm < 0.0 || mm > 2.0)
	//   System.err.println("ERROR MM "+mm);
	
	// night length as fraction
	double b = 1.0 - Math.toDegrees(Math.acos(1.0 - mm))/180.0;
	
	return b*86400000.0; // in ms
	
    }


}
